package services

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/6tail/lunar-go/calendar"
	"sanrenx.com/xyxf/app/daos"
	"sanrenx.com/xyxf/app/models"
	"sanrenx.com/xyxf/app/models/column"
	"sanrenx.com/xyxf/app/services/cache"
	"sanrenx.com/xyxf/pkg/e"
	"sanrenx.com/xyxf/pkg/gredis"
	"sanrenx.com/xyxf/pkg/logging"
	"sanrenx.com/xyxf/pkg/orm"
	"strconv"
	"time"
)

/**
 * 用户服务
 * @Description:
 */
type UserSrv struct{}

/**
 * @Description: 小程序认证授权
 * @receiver srv
 * @param openid  小程序 openid
 * @param unionid 小程序 unionid
 * @param platform 小程序平台
 * @return bool 是否新用户
 * @return *models.UserNewbie 用户信息
 * @return error 异常信息
 */
func (srv *UserSrv) LoginByMP(openid string, unionid string, platform string) (exists bool, data *models.UserNewbie, err error) {

	//开启事务
	tx := orm.DB.Begin()
	defer func() {
		if rMsg := recover(); rMsg != nil {
			logging.Error(rMsg)
			err = errors.New(fmt.Sprintf("%v", rMsg))
			tx.Rollback()
		}
	}()

	//用户
	userDao := daos.UserDao{DB: tx}
	//openid 查重
	exists, _ = userDao.ExitUserByOpenID(openid, platform)

	//获取第一个匹配的记录，或创建一个具有给定条件的新记录
	if data, err = userDao.FirstOrCreateUser(map[string]interface{}{
		"openid":   openid,
		"platform": platform,
	}, map[string]interface{}{
		"unionid": unionid,
	}); err != nil {
		tx.Rollback()
		return exists, nil, err
	}

	//新用户初始奖励
	if !exists {
		////注册 金币奖励
		if _, err := userDao.SaveValColumn(column.Coin_ZhuCe, data.ID, data.ID); err != nil {
			tx.Rollback()
			return exists, nil, err
		}

		//注册 经验奖励
		if _, err := userDao.SaveValColumn(column.Exp_ZhuCe, data.ID, data.ID); err != nil {
			tx.Rollback()
			return exists, nil, err
		}
	}

	//提交事务
	if err := tx.Commit().Error; err != nil {
		return exists, nil, err
	}

	return exists, data, err
}

/**
 * @Description: 获取用户详情
 * @receiver srv
 * @param uid 用户ID
 * @return data 用户信息
 * @return err 异常信息
 */
func (srv *UserSrv) GetUserById(id uint64) (data *models.UserDetails, err error) {

	var cacheSrv cache.UserCache
	//查找Redis缓存
	dataKey := cacheSrv.GetDataKey(id)
	ctx := context.Background()
	if gredis.Exist(ctx, dataKey) {
		cacheData, err := gredis.Get(ctx, dataKey)
		if err != nil {
			logging.Info(err)
		} else {
			_ = json.Unmarshal([]byte(cacheData), &data)
			return data, nil
		}
	}

	//刷新缓存，同时获取数据
	if data, err = srv.RefreshCacheUserById(id); err != nil {
		return nil, err
	}

	return data, nil
}

/**
 * @Description: 用户收藏
 * @receiver srv
 * @param uid 用户ID
 * @param id 数据ID
 * @param mode 数据模型
 * @return data 收藏
 * @return err 异常信息
 */
func (srv *UserSrv) GetFav(uid uint64, id uint64, mode string) (data *models.UserAction, err error) {

	var cacheSrv cache.UserCache
	//构造缓存KEY
	ctx := context.Background()
	dataKey := cacheSrv.GetFavKey(uid, mode)
	key := strconv.FormatUint(id, 10)
	//检查缓存中是否存在
	if gredis.HExist(ctx, dataKey, key) {
		cacheData, err := gredis.HGet(ctx, dataKey, key)
		if err != nil {
			logging.Info(err)
		} else {
			_ = json.Unmarshal([]byte(cacheData), &data)
			return data, nil
		}
	}

	//从数据库中检索
	modelDao := daos.UserDao{DB: orm.DB}
	if data, err = modelDao.GetUserFav(uid, id, mode); err != nil {
		return nil, err
	}

	//加入缓存
	if data != nil {
		_ = gredis.HSet(ctx, dataKey, key, data)
	}
	return data, nil
}

/**
 * @Description: 用户收藏
 * @receiver srv
 * @param uid 用户ID
 * @param id 数据ID
 * @param mode 数据模型
 * @return data 收藏
 * @return err 异常信息
 */
func (srv *UserSrv) GetPraise(uid uint64, id uint64, mode string) (data *models.UserAction, err error) {

	var cacheSrv cache.UserCache
	//缓存KEY
	ctx := context.Background()
	dataKey := cacheSrv.GetPraiseKey(uid, mode)
	key := strconv.FormatUint(id, 10)
	//检查缓存中是否存在
	if gredis.HExist(ctx, dataKey, key) {
		cacheData, err := gredis.HGet(ctx, dataKey, key)
		if err != nil {
			logging.Info(err)
		} else {
			_ = json.Unmarshal([]byte(cacheData), &data)
			return data, nil
		}
	}

	//从数据库中检索
	modelDao := daos.UserDao{DB: orm.DB}
	if data, err = modelDao.GetUserPraise(uid, id, mode); err != nil {
		return nil, err
	}

	//加入缓存
	if data != nil {
		_ = gredis.HSet(ctx, dataKey, key, data)
	}

	return data, nil
}

/**
 * @Description: 获取日修行记录
 * @receiver srv
 * @param uid 用户ID
 * @param mode 类型
 * @return list 修行记录
 * @return err 异常信息
 */
func (srv *UserSrv) GetStdDaily(uid uint64, ymd string) (data *models.UserDaily, err error) {

	var cacheSrv cache.UserCache
	//查找Redis缓存
	dataKey := cacheSrv.GetDailyKey(uid, ymd)
	ctx := context.Background()
	if gredis.Exist(ctx, dataKey) {
		cacheData, err := gredis.Get(ctx, dataKey)
		if err != nil {
			logging.Info(err)
		} else {
			_ = json.Unmarshal([]byte(cacheData), &data)
			return data, nil
		}
	}

	//刷新缓存，同时获取数据
	if data, err = srv.RefreshCacheUserDaily(uid, ymd); err != nil {
		return nil, err
	}

	return data, nil
}

/**
 * @Description: 用户签到
 * @receiver srv
 * @param uid 用户ID
 * @return code 结果码
 * @return res 奖励信息
 * @return err 异常信息
 */
func (srv *UserSrv) SaveUserSign(uid uint64) (code int, res *models.Data, err error) {

	nowTime := calendar.NewSolarFromDate(time.Now())
	if data, err := srv.GetStdDaily(uid, nowTime.ToYmd()); err != nil || data.QianDao != nil {
		//如果有签到数据
		if data != nil && data.QianDao != nil {
			err = errors.New("重复签到")
		}
		return e.ErrorAlreadySignIn, nil, err
	}

	//开启事务
	tx := orm.DB.Begin()
	defer func() {
		if rMsg := recover(); rMsg != nil {
			logging.Error(rMsg)
			tx.Rollback()
		}
	}()

	modelDao := daos.UserDao{DB: tx}
	//更新 修行统计
	if err = modelDao.SaveStdColumn(column.Std_QianDao, uid, nowTime.ToYmd()); err != nil {
		//出现异常回滚
		logging.Warn(err)
		tx.Rollback()
		return e.ErrorSignInFail, nil, err
	}

	//更新 奖励佛缘点
	var coinVU models.VU
	if coinVU, err = modelDao.SaveValColumn(column.Coin_QianDao, uid, nowTime); err != nil {
		//出现异常回滚
		logging.Warn(err)
		tx.Rollback()
		return e.ErrorSignInFail, nil, err
	}

	//更新 奖励经验
	var expVU models.VU
	if expVU, err = modelDao.SaveValColumn(column.Exp_QianDao, uid, nowTime); err != nil {
		//出现异常回滚
		logging.Warn(err)
		tx.Rollback()
		return e.ErrorSignInFail, nil, err
	}

	//提交
	if err := tx.Commit().Error; err != nil {
		return e.ERROR, nil, err
	}

	return e.SUCCESS, &models.Data{Coin: coinVU, Exp: expVU}, nil
}

/**
 * @Description: 刷新用户缓存数据
 * @receiver srv
 * @param id 用户ID
 * @return data 用户
 * @return err 异常信息
 */
func (srv *UserSrv) RefreshCacheUserById(id uint64) (data *models.UserDetails, err error) {

	modelDao := daos.UserDao{DB: orm.DB}
	//根据ID查找数据库获取文章
	if data, err = modelDao.GetUser(id); err != nil {
		logging.Error(err)
		return nil, err
	}

	//更新缓存
	var cacheSrv cache.UserCache
	dataKey := cacheSrv.GetDataKey(id)
	//写一个String缓存, 过期时间1小时
	ctx := context.Background()
	_ = gredis.Set(ctx, dataKey, data, cache.UserExpireTime)
	return data, nil
}

/**
 * @Description: 刷新用户日统计数据
 * @receiver srv
 * @param userID 用户ID
 * @param ymd 日期
 * @return data 日统计
 * @return err 异常信息
 */
func (srv *UserSrv) RefreshCacheUserDaily(userID uint64, ymd string) (data *models.UserDaily, err error) {
	var cacheSrv cache.UserCache
	modelDao := daos.UserDao{DB: orm.DB}
	//根据ID查找数据库获取
	if data, err = modelDao.GetUserDaily(map[string]interface{}{
		"user_id": userID,
		"ymd":     ymd,
	}); err != nil {
		logging.Error(err)
		return nil, err
	}

	//更新缓存
	dataKey := cacheSrv.GetDailyKey(userID, ymd)
	//写一个String缓存, 过期时间1小时
	ctx := context.Background()
	_ = gredis.Set(ctx, dataKey, data, cache.UserDailyExpireTime)

	return data, nil
}
